CVE-2017-8890漏洞分析
2017年神洞。一直都想玩玩,前段时间分析了下漏洞原理,尝试着写漏洞利用,奈何一直堆喷不成功。哎,水平还是不够,继续修炼。分析过程就发出来吧。不喜勿喷。看了几个linux网络方面的漏洞,发现linux中用C语言实现面对对象编程真是够有意思的,尤其是找每个对应协议的回调函数时,不太好找,或许对linux网络协议代码还是不够熟悉吧。
基于linux-4.1源码分析
第一步,找出来mc_list第一次被初始化的函数。全局搜索mc_list的引用。
static int inet_create(struct net *net, struct socket *sock, int protocol,
int kern)
在inet_create函数中发现:
这里只是初始化为NULL。
为什么会确定就是inet_create函数中第一次被创建,因为inet_create函数是在应用层调用socket函数创建socket对象时走到内核层里要调用的。动态调试看。
看一下inet的值。
将mc_list的地址打印出来。
可以看到当前mc_list指针是NULL,存放mc_list指针的地址为0xffff88003cfce300
开始下内存访问断点。
跑起来,断下来了。如下图:
果然是在调用setsockopt设置组播模式这个过程中访问的。
看一下源码:
果然是对mc_list进行rcu模式的解引用。
继续向下看源码。
Rcu_assign_pointer这个其实rcu模式的拷贝函数。红框中代码就是将iml赋值给inet_mc_list。
单步走看看iml的值。
Sock_kmalloc后,iml的值是0xffff88003c8ad80。继续单步。
将inet->mc_list赋值给Iml->next_rcu,这个时候inet->mc_list仍然是NULL。
继续单步。
即将执行1898行代码了。
单步过后。
已经赋值了。到这里,算是inet->mc_list被第一次赋值了。这个值就是0xffff88003c8a4d0。
看一下inet->mc_list结构体里的数据。
0xa0a02e0就是poc中的组网地址:10.10.2.224/0a:0a:02:e0。
到此,算是找出mc_list对象第一次被赋值。这个对象大小是0x30个字节。
第二步:
Diff 可以找到补丁函数。
net/ipv4/inet_connection_sock.c:707
struct sock *inet_csk_clone_lock(const struct sock *sk,
const struct request_sock *req,
const gfp_t priority)
其实可以通过understand去找这个函数的调用链,在自动生成的过程中,发现找的不完整。故采取动态调试查看调用链。
接下来在inet_csk_clone_lock上下断点。
主要是tcp_v4_syn_recv_sock在tcp_check_req这个函数的调用过程中是属于回调函数,故必须调试查看。查看如下。
从源码中可以找到调用的地方,下面通过打印结构体进行确认。
先确定从第一步到漏洞函数调用链。
在inet_csk_clone_lock中。
看一下补丁。
这里需要确定newsk了。mc_list是一样的。这里就是sk_clone_lock拷贝的过程。
这里父对象是0xffff88003cfce000,子对象是0xffff88003cfcf000。为什么要确定这两个对象。方便分析为什么poc中要调用accept函数。
第三步:
确定accept函数调用链。
看一下源码。
在队列中查询已经建立的连接,并找出它们对应的sock对象。
Newsk其实就是前面三次握手后sk_clone_lock拷贝产生的子对象。在应用层调用accept函数,可以将这个newsk对象对应的应用层socket句柄返回给用户进行操作。
第四步:
Ok。到此,就剩下释放了。Poc中释放的代码如下:
Poc中写的是先释放子对象后释放父对象。这里就方便了,应为对mc_list下了内存访问断点,直接跑起来就可以看到结果了。
调用过程。
看一下源码:
调试到这里时候崩溃了,环境重新建立,有些数据结构肯定发生变化。
重新调试来过。
确定父对象和子对象。
这里0xffff8800cd46000是父对象,0xffff88003cd47000是子对象。
Mc_list对象如下图:
这里为避免发送意外,直接在ip_mc_drop_socket上面下断点,也可以同时下个内存断点。
讲道理肯定是在ip_mc_drop_socket上先断下来。
先释放子对象。 后面直接跑起来程序,又一次断在了ip_mc_drop_socket上面,如下图:
接着释放父对象。下面就是二次释放了。到此,漏洞原理分析完毕。
第五步:如何利用。
采取堆喷占位。这里看一下mc_list结构体:
Rcu_head结构体:
Rcu_head结构体里面的func,函数指针。控制这个函数指针就可以控制EIP了。还有一个问题,这个func在哪里调用?
这个rcu_head其实留给rcu机制在释放资源时用于回调的。这里不展开讲rcu机制。通俗点讲,其实在kfree_rcu函数中只是给将要释放的资源记录一下并不是真正去释放,这里面有个宽限期的概念,然后发送一个软中断给系统。然后rcu机制监听到中断后,相关相应函数开始工作。具体调用流程如下。
最终可以在__rcu_reclaim函数中有对func的调用。
Ok。
其实思路都很简单,讲出来或许大家都懂。但是在进行实际堆喷一直都不成功。我是采用多进程进行堆喷,一直提示setsockopt设置参数非法,一时比较懵圈。抽空再折腾折腾吧。
贴出POC,也是东家看,西家借鉴。各位高手请不吝指教。
看雪ID:ID蝴蝶
bbs.pediy.com/user-594134
本文由看雪论坛 ID蝴蝶 原创
转载请注明来自看雪社区